package jp.ac.u_tokyo.dch.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import jp.ac.u_tokyo.dch.core.db.DBConnection;
import jp.ac.u_tokyo.dch.core.exceptions.DBException;
import jp.ac.u_tokyo.dch.dao.common.SeqenceGenerator;
import jp.ac.u_tokyo.dch.dao.data.DataDao;
import jp.ac.u_tokyo.dch.dao.data.DataDetailDao;
import jp.ac.u_tokyo.dch.dao.schema.DataSchemaDetailDao;
import jp.ac.u_tokyo.dch.dao.xmloutput.OutputAttrDao;
import jp.ac.u_tokyo.dch.dao.xmloutput.OutputTagDao;
import jp.ac.u_tokyo.dch.model.SchemaGroup;
import jp.ac.u_tokyo.dch.model.db.Data;
import jp.ac.u_tokyo.dch.model.db.DataDetail;
import jp.ac.u_tokyo.dch.model.db.DataItem;
import jp.ac.u_tokyo.dch.model.db.DataSchemaDetail;
import jp.ac.u_tokyo.dch.model.output.Attribute;
import jp.ac.u_tokyo.dch.model.output.XmlComposite;
import jp.ac.u_tokyo.dch.model.output.XmlData;
import jp.ac.u_tokyo.dch.util.common.XmlOutputUtil;
import jp.ac.u_tokyo.dch.view.Constants;
import jp.ac.u_tokyo.dch.view.conv.ConvView;

import org.apache.commons.lang.xwork.StringEscapeUtils;
import org.apache.commons.lang.xwork.StringUtils;

public class ConvService {

	private final static String HEAD = "<?xml version=\"%s\" encoding=\"%s\"?>";
	/**
	* DBコネクションオブジェクト
	 */
	private DBConnection dbconn = null;


	/**
	 * オブジェクト生成者
	 * @param dbconn
	 */
	public ConvService(DBConnection dbconn){
		this.dbconn = dbconn;
	}

	public void insert(ConvView view) throws DBException {

		OutputTagDao outputTagDao = new OutputTagDao(dbconn);
		OutputAttrDao outputAttrDao = new OutputAttrDao(dbconn);


		outputTagDao.delete(view.getKeySchemaid());
		outputAttrDao.delete(view.getKeySchemaid());

		int i = 1;

		for (XmlData xmlData : view.getXmlObjList()) {

			outputTagDao.insert(i, view.getKeySchemaid(), xmlData);

			if (xmlData.getAttrList() != null) {
				outputAttrDao.insert(view.getKeySchemaid(), xmlData.getTagId(), xmlData.getAttrList());
			}
			i++;
		}
	}

	public String outputXml(SchemaGroup schemaGroup,
			String lv1Dataid,
			String lv2Dataid,
			String lv3Dataid,
			String version,
			String encoding,
			String rootTagName,
			List<Attribute> rootAttrList) throws DBException {

		DataDao dataDao = new DataDao(dbconn);

		DataDetailDao dataDetailDao = new DataDetailDao(dbconn);

		String lv1Schemaid = schemaGroup.getLv1Schemaid();
		String lv2Schemaid = schemaGroup.getLv2Schemaid();
		String lv3Schemaid = schemaGroup.getLv3Schemaid();
		String lv4Schemaid = schemaGroup.getLv4Schemaid();
		String lv5Schemaid = schemaGroup.getLv5Schemaid();
		String lv6Schemaid = schemaGroup.getLv6Schemaid();


		XmlComposite composite1 = XmlOutputUtil.xmlObjListToXmlDisObjForOutput(selectXmlDataForOutput(lv1Schemaid));
		XmlComposite composite2 = XmlOutputUtil.xmlObjListToXmlDisObjForOutput(selectXmlDataForOutput(lv2Schemaid));
		XmlComposite composite3 = XmlOutputUtil.xmlObjListToXmlDisObjForOutput(selectXmlDataForOutput(lv3Schemaid));
		XmlComposite composite4 = XmlOutputUtil.xmlObjListToXmlDisObjForOutput(selectXmlDataForOutput(lv4Schemaid));
		XmlComposite composite5 = XmlOutputUtil.xmlObjListToXmlDisObjForOutput(selectXmlDataForOutput(lv5Schemaid));
		XmlComposite composite6 = XmlOutputUtil.xmlObjListToXmlDisObjForOutput(selectXmlDataForOutput(lv6Schemaid));

		StringBuilder xml = new StringBuilder();
		xml.append(String.format(HEAD, version, encoding));

		xml.append("<");
		xml.append(rootTagName);

		for (Attribute attribute : rootAttrList) {

			xml.append(" ");
			xml.append(attribute.getName());
			xml.append("=");
			xml.append("\"");
			xml.append(attribute.getValue());
			xml.append("\"");
		}
		xml.append(">");

		Map<String, DataDetail> detailMap = dataDetailDao.selectL3ByIdForMap(lv1Dataid, lv2Dataid, lv3Dataid);

		//作成機関
		StringBuilder lv123xml = new StringBuilder();
		lv123xml.append(convertResult(composite1.toXml(), lv1Schemaid, detailMap));
		//目録データ概要
		lv123xml.append(convertResult(composite2.toXml(), lv2Schemaid, detailMap));
		//資料群全体
		lv123xml.append(convertResult(composite3.toXml(), lv3Schemaid, detailMap));

		String xmlTemp;

		List<Data> lv4DataList = dataDao.selectChildData(lv3Dataid, Constants.LV4_SORT, lv4Schemaid);

		//サブフォンド
		StringBuilder lv4xml = new StringBuilder();

		String lv4XmlTemplate = composite4.toXml();
		String lv5XmlTemplate = composite5.toXml();
		String lv6XmlTemplate = composite6.toXml();

		for (Data lv4Data : lv4DataList) {

			Map<String, DataDetail> lv4detailMap = new HashMap<String, DataDetail>();

			lv4detailMap.putAll(detailMap);
			lv4detailMap.putAll(dataDetailDao.selectByIdForMap(lv4Data.getId()));

			lv4xml.append(convertResult(lv4XmlTemplate, lv4Schemaid, lv4detailMap));

			List<Data> lv5DataList = dataDao.selectChildData(lv4Data.getId(), Constants.LV5_SORT, lv5Schemaid);

			//シリーズ
			StringBuilder lv5xml = new StringBuilder();

			for (Data lv5Data : lv5DataList) {

				Map<String, DataDetail> lv5detailMap = new HashMap<String, DataDetail>();

				lv5detailMap.putAll(detailMap);
				lv5detailMap.putAll(lv4detailMap);
				lv5detailMap.putAll(dataDetailDao.selectByIdForMap(lv5Data.getId()));

				lv5xml.append(convertResult(lv5XmlTemplate, lv5Schemaid, lv5detailMap));

				List<Data> lv6DataList = dataDao.selectChildData(lv5Data.getId(), Constants.LV6_SORT, lv6Schemaid);
				//目録データ概要
				StringBuilder lv6xml = new StringBuilder();
				for (Data lv6Data : lv6DataList) {

					Map<String, DataDetail> lv6detailMap = new HashMap<String, DataDetail>();

					lv6detailMap.putAll(detailMap);
					lv6detailMap.putAll(lv4detailMap);
					lv6detailMap.putAll(lv5detailMap);
					lv6detailMap.putAll(dataDetailDao.selectByIdForMap(lv6Data.getId()));

					lv6xml.append(convertResult(lv6XmlTemplate, lv6Schemaid, lv6detailMap));
				}

				String temp5 = StringUtils.replace(lv5xml.toString(), Constants.TAG_INSERT_ID , lv6xml.toString());

				lv5xml.delete(0, lv5xml.length());
				lv5xml.append(temp5);
			}
			String temp4 = StringUtils.replace(lv4xml.toString(), Constants.TAG_INSERT_ID , lv5xml.toString());
			lv4xml.delete(0, lv4xml.length());
			lv4xml.append(temp4);
		}

		xmlTemp = StringUtils.replace(lv123xml.toString(), Constants.TAG_INSERT_ID , lv4xml.toString());

		xml.append(xmlTemp);

		xml.append("</");
		xml.append(rootTagName);
		xml.append(">");

		return xml.toString();


	}

	/**
	 *
	 * @param xml
	 * @param dataid
	 * @param schemaid
	 * @return
	 * @throws DBException
	 */
	private String convertResult(String xml, String schemaid, Map<String, DataDetail> detailMap) throws DBException {

		DataSchemaDetailDao dataSchemaDetailDao = new DataSchemaDetailDao(dbconn);

		List<DataSchemaDetail> list = dataSchemaDetailDao.selectList(schemaid);


		for (DataSchemaDetail schemaDetail : list) {

			String fieldKey = schemaDetail.getFieldKey();
			String search = "{" + fieldKey + "}";

			DataDetail dataDetail = detailMap.get(fieldKey);

			if (dataDetail != null) {
				if (Constants.DATA_TYPE_TEXTAREA.equals(dataDetail.getDataType())) {

					xml = StringUtils.replace(xml, search, StringEscapeUtils.escapeXml(dataDetail.getList().get(0).getDbAreaData()));

				} else if (Constants.DATA_TYPE_TEXT.equals(dataDetail.getDataType())
						|| Constants.DATA_TYPE_ONE.equals(dataDetail.getDataType())
						|| Constants.DATA_TYPE_ONE_FREE.equals(dataDetail.getDataType())) {

					xml = StringUtils.replace(xml, search, StringEscapeUtils.escapeXml(dataDetail.getList().get(0).getDbData()));

				} else if (Constants.DATA_TYPE_MULTY.equals(dataDetail.getDataType())
						|| Constants.DATA_TYPE_MULTY_FREE.equals(dataDetail.getDataType())) {

					List<String> tempLinkList = new ArrayList<String>();
					for (DataItem item : dataDetail.getList()) {

						tempLinkList.add(item.getDbData());
					}
					xml = StringUtils.replace(xml, search, StringEscapeUtils.escapeXml(StringUtils.join(tempLinkList, ",")));

				} else if (Constants.DATA_TYPE_FIELDSELECT.equals(dataDetail.getDataType())) {

					xml = StringUtils.replace(xml, search, StringEscapeUtils.escapeXml(findFieldData(dataDetail.getList().get(0).getDbData(), detailMap)));
				} else if (Constants.DATA_TYPE_MULTI_TEXT.equals(dataDetail.getDataType())) {

					List<String> matchList = getSearchTagElement(fieldKey, xml);

					for (String match : matchList) {

						StringBuilder tagList = new StringBuilder();

						for (DataItem item : dataDetail.getList()) {

							tagList.append(StringUtils.replace(match, search, item.getDbData()));
						}

						xml = StringUtils.replace(xml, match, tagList.toString());
					}

				} else if (Constants.DATA_TYPE_NOT_INPUT.equals(dataDetail.getDataType())) {

					xml = StringUtils.replace(xml, search, "");
				} else {
					xml = StringUtils.replace(xml, search, StringEscapeUtils.escapeXml(dataDetail.getDbData()));
				}
			} else {
				xml = StringUtils.replace(xml, search, "");
			}
		}

		return xml;
	}

	/**
	 * XMLデータリストを取得する
	 * @param schemaid スキーマID
	 * @return XMLデータリスト
	 * @throws DBException 例外
	 */
	public List<XmlData> selectXmlData(String schemaid) throws DBException {

		OutputTagDao outputTagDao = new OutputTagDao(dbconn);

		List<XmlData> list = outputTagDao.selectXmlData(schemaid);

		if (list.isEmpty()) {

			SeqenceGenerator generator = new SeqenceGenerator(dbconn);

			int tagId = generator.getSequence(SeqenceGenerator.TAG_SEQ);

			XmlData root = new XmlData();

			root.setTagId(String.valueOf(tagId));
			root.setOyaTagId(String.valueOf(0));
			root.setLevel("0");
			root.setTagName("");
			root.setIndex("0");
			root.setType(Constants.TAG_TYPE_TEXT);
			list.add(root);
		}

		return list;
	}

	/**
	 * XMLデータリストを取得する
	 * @param schemaid スキーマID
	 * @return XMLデータリスト
	 * @throws DBException 例外
	 */
	public List<XmlData> selectXmlDataForOutput(String schemaid) throws DBException {

		OutputTagDao outputTagDao = new OutputTagDao(dbconn);

		List<XmlData> list = outputTagDao.selectXmlData(schemaid);

		return list;
	}

	/**
	 *
	 * @param xml
	 * @return
	 */
	private List<String> getSearchTagElement(String key, String xml) {

		List<String> matchTag = new ArrayList<String>();

		Pattern pattern = Pattern.compile("<*[^>]*>\\{" + key + "\\}<\\/*[^>]*>");

		Matcher matcher = pattern.matcher(xml);

		while (matcher.find()) {
			matchTag.add(matcher.group());
		}
		return matchTag;

	}

	/**
	 * 指定したフィールドを探す。
	 * @param fieldKey
	 * @param dataItem
	 * @return
	 */
	private static String findFieldData(String fieldKey, Map<String, DataDetail> dataDetail) {

		String data = "";

		DataDetail item = dataDetail.get(fieldKey);

		if (item == null) {

			return data;
		}

		String selectFieldKey = item.getDbData();

		for (item = dataDetail.get(selectFieldKey); item != null && Constants.DATA_TYPE_FIELDSELECT.equals(item.getDataType());) {

			item = dataDetail.get(item.getDbData());
		}

		if (item != null) {

			if (Constants.DATA_TYPE_TEXTAREA.equals(item.getDataType())) {
				data = item.getDbAreaData();
			} else {
				data = item.getDbData();
			}
		}

		return data;
	}


}
